﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using ProceduralGolf.geometry;

namespace ProceduralGolf.generation
{
    class GrassGenerator
    {
        Game game;
        Texture2D[] textures;
        //VertexBuffer vertexBuffer;
        //IndexBuffer indexBuffer;
        public static float WIDTH = 32;
        public static float HEIGHT = 8;

        public GrassGenerator(Game game)
        {
            this.game = game;
            //GenerateGrass();
        }


        private void DrawGrass(ref Color[] data, int width, int height, Vector2 start, Vector2 end, Color color, int seg)
        {
            float x0 = start.X;
            float y0 = start.Y;
            
            for (int i = 1; i <= seg; i++)
            {
                float x1 = MathHelper.Lerp(start.X, end.X, (float)Math.Pow(i / (float)seg, 2));
                float y1 = MathHelper.Lerp(start.Y, end.Y, i / (float)seg);
                DrawLine(ref data, width, height, new Vector2(x0, y0), new Vector2(x1, y1), color);
                x0 = x1;
                y0 = y1;
            }
        }

        public Texture2D generateGrassTexture(int width, int height, float grassHeight, int amount, Color c1, Color c2)
        {
            Random random = new Random();
            Color[] data = new Color[width*height];

            for (int i = 0; i < data.Length; ++i)
            {
                data[i] = Color.Lerp(c1, c2, (float)random.NextDouble());
                data[i].A = 0;
            }

            int seg = 3;
            float dev = 20.0f;

            for (int i = 0; i < amount; i++)
            {
                float x = width * (float)random.NextDouble();
                float y = height;
                float w = MathHelper.Lerp(-dev, dev, (float)random.NextDouble());
                float h = MathHelper.Lerp(height*grassHeight*0.25f, height*grassHeight, (float)random.NextDouble());
                Color c = Color.Lerp(c1, c2, (float)random.NextDouble());
                DrawGrass(ref data, width, height, new Vector2(x, y), new Vector2(x + w, y - h), c, seg);
            }

            Texture2D tex = new Texture2D(game.GraphicsDevice, width, height);
            tex.SetData(data);
            return tex;

        }

        private void DrawLine(ref Color[] data, int width, int height, Vector2 start, Vector2 end, Color color)
        {
            int x0 = (int)Math.Round(start.X);
            int x1 = (int)Math.Round(end.X);
            int y0 = (int)Math.Round(start.Y);
            int y1 = (int)Math.Round(end.Y);
            bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
            if (steep)
            {
                int tmp = x0;
                x0 = y0;
                y0 = tmp;
                
                tmp = x1;
                x1 = y1;
                y1 = tmp;
            }
            if (x0 > x1)
            {
                int tmp = x0;
                x0 = x1;
                x1 = tmp;

                tmp = y0;
                y0 = y1;
                y1 = tmp;
            }
            int dx = x1 - x0;
            int dy = Math.Abs(y1 - y0);

            int error = dx / 2;
            int ystep = (y0 < y1) ? 1 : -1;
            int y = y0;
            for (int x = x0; x <= x1; x++)
            {
                if (steep)
                {
                    //teken op (y,x) en enkel op tex
                    if (!(y < 0 || y >= width || x < 0 || x >= height))
                        data[y + x * width] = color;
                }
                else
                {
                    //teken op (x,y) en enkel op tex
                    if (!(x < 0 || x >= width || y < 0 || y >= height))
                        data[x + y * width] = color;
                }
                error -= dy;
                if (error < 0)
                {
                    y += ystep;
                    error += dx;
                }
            }
        }

        
        //void GenerateGrass()
        //{
        //    vertexBuffer = new VertexBuffer(game.GraphicsDevice, typeof(VertexPositionTexture), Grass.NVERTICES, BufferUsage.WriteOnly);
        //    indexBuffer = new IndexBuffer(game.GraphicsDevice, typeof(short), Grass.NINDICES, BufferUsage.WriteOnly);


        //    VertexPositionTexture[] vertices = new VertexPositionTexture[Grass.NVERTICES];

        //    Vector3 position0 = new Vector3(-WIDTH/2.0f, 0.0f, 0.0f);
        //    Vector3 position1 = new Vector3(-WIDTH / 2.0f, HEIGHT, 0.0f);
        //    Vector3 position2 = new Vector3(WIDTH / 2.0f, HEIGHT, 0.0f);
        //    Vector3 position3 = new Vector3(WIDTH / 2.0f, 0.0f, 0.0f);


        //    vertices[0] = new VertexPositionTexture(position0, Vector2.UnitY);
        //    vertices[1] = new VertexPositionTexture(position1, Vector2.Zero);
        //    vertices[2] = new VertexPositionTexture(position2, Vector2.UnitX);
        //    vertices[3] = new VertexPositionTexture(position3, Vector2.One);

        //    vertexBuffer.SetData(vertices);
        //    short[] indices = {0,1,2,2,3,0,0,3,2,2,1,0};
        //    indexBuffer.SetData(indices);
        //}

        public Grass[] getGrass(GolfCourse course)
        {
            Texture2D[] textures = new Texture2D[2];
            textures[0] = generateGrassTexture(400, 100, 0.75f, 100, new Color(0.04f, 0.25f, 0.0f, 1.0f), new Color(0.12f, 0.45f, 0.0f, 1.0f));
            //textures[1] = generateGrassTexture(400, 100, 1.0f, 50, new Color(0, 64, 0), new Color(0, 192, 0));
            textures[1] = generateGrassTexture(400, 100, 0.25f, 100, new Color(0.03f, 0.30f, 0.0f, 1.0f), new Color(0.10f, 0.6f, 0.0f, 1.0f));
            
            Grass[] grass = new Grass[textures.Length];
            grass[0] = getGrass(course, GolfCourseLayout.GolfCourseLayer.ROUGH, 0.05f, 3, textures[0]);
            //grass[1] = getGrass(course, GolfCourseLayout.GolfCourseLayer.ROUGH, 0.025f, 3, textures[1]);
            grass[1] = getGrass(course, GolfCourseLayout.GolfCourseLayer.FRINGE, 0.1f, 3, textures[1]);
            return grass;
        }


        Grass getGrass(GolfCourse course, GolfCourseLayout.GolfCourseLayer layer, float amount, int parts, Texture2D texture)
        {
            VertexBuffer[] vertexBuffers = new VertexBuffer[parts];
            IndexBuffer[] indexBuffers = new IndexBuffer[parts];
            List<Vector3> positions = new List<Vector3>();
            Random random = new Random();
            for (int x = 0; x < course.Heightmap.Width-1; ++x)
            {
                for (int y = 0; y < course.Heightmap.Height-1; ++y)
                {
                    if (course.Layermap[x, y] == layer && (float)random.NextDouble() < amount)
                    {
                        float height = (course.Heightmap.getHeight(x, y) + course.Heightmap.getHeight(x + 1, y) + course.Heightmap.getHeight(x, y + 1) + course.Heightmap.getHeight(x + 1, y + 1)) / 4;
                        positions.Add(new Vector3(x + 0.5f, height, y + 0.5f)*course.Scale);
                    }
                }
            }
            Vector3[] grassPositions = positions.ToArray();
            float[] angles = new float[grassPositions.Length];
            for (int i = 0; i < angles.Length; ++i)
                angles[i] = (float)random.NextDouble() * MathHelper.TwoPi;
            
            Vector3 position0 = new Vector3(-WIDTH / 2.0f, 0.0f, 0.0f);
            Vector3 position1 = new Vector3(-WIDTH / 2.0f, HEIGHT, 0.0f);
            Vector3 position2 = new Vector3(WIDTH / 2.0f, HEIGHT, 0.0f);
            Vector3 position3 = new Vector3(WIDTH / 2.0f, 0.0f, 0.0f);

            int[] grassIndices = { 0, 1, 2, 2, 3, 0, 0, 3, 2, 2, 1, 0 };

            for (int part = 0; part < parts; ++part)
            {
                VertexBuffer vertexBuffer = new VertexBuffer(game.GraphicsDevice, typeof(VertexPositionTexture), Grass.NVERTICES * grassPositions.Length, BufferUsage.WriteOnly);
                IndexBuffer indexBuffer = new IndexBuffer(game.GraphicsDevice, typeof(int), Grass.NINDICES * grassPositions.Length, BufferUsage.WriteOnly);
                VertexPositionTexture[] vertices = new VertexPositionTexture[Grass.NVERTICES * grassPositions.Length];
                int[] indices = new int[grassIndices.Length * grassPositions.Length];

                float angle = part * (MathHelper.TwoPi / parts);

                for (int i = 0; i < grassPositions.Length; ++i)
                {
                    Matrix rotationMatrix = Matrix.CreateRotationY(angle + angles[i]);
                    vertices[i * 4 + 0] = new VertexPositionTexture(Vector3.Transform(position0, rotationMatrix) + grassPositions[i], Vector2.UnitY);
                    vertices[i * 4 + 1] = new VertexPositionTexture(Vector3.Transform(position1, rotationMatrix) + grassPositions[i], Vector2.Zero);
                    vertices[i * 4 + 2] = new VertexPositionTexture(Vector3.Transform(position2, rotationMatrix) + grassPositions[i], Vector2.UnitX);
                    vertices[i * 4 + 3] = new VertexPositionTexture(Vector3.Transform(position3, rotationMatrix) + grassPositions[i], Vector2.One);

                    for (int j = 0; j < grassIndices.Length; ++j)
                    {
                        indices[i * grassIndices.Length + j] = grassIndices[j] + i * 4;
                    }
                }

                vertexBuffer.SetData(vertices);
                indexBuffer.SetData(indices);
                vertexBuffers[part] = vertexBuffer;
                indexBuffers[part] = indexBuffer;
            }
            
            return new Grass(game, grassPositions, vertexBuffers, indexBuffers, texture);
        }
    }
}
